//
//      Author: Alexandros Sigalas
//
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; either version 2 of the License, or
//      (at your option) any later version.
//      
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//      
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.

#include <stdlib.h>
#include <assert.h>
#include "mesh.h"
#include "bounds.h"
#include "mathutils.h"
#include "polygon.h"
#include "islands.h"

mesh me_init(void)
{
    mesh me= (mesh)calloc(1, sizeof(struct Mesh));
    assert(me);
    return me;
}

void me_kill(mesh *me_pt)
{
    assert(me_pt);
    mesh me= *me_pt;
    if (me){
        if (me->faces)
            free(me->faces);
        if (me->edges)
            free(me->edges);
        if (me->verts)
            free(me->verts);
        free(me);
        *me_pt= NULL;
    }
}

void me_recalc_bounds(mesh me)
{
    assert(me);
    v_copy(&me->bb.min, &me->verts[0].co);
    v_copy(&me->bb.max, &me->verts[0].co);
    uint i;
    for (i=1; i < me->last_v; i++)
        check_bounds(&me->bb, &me->verts[i].co);
}

static void me_expand_verts(mesh me, uint count)
{
    assert(me);
    assert(count);
    me->size_v+= count;
    if (me->verts)
        me->verts= (mvert)realloc(me->verts, me->size_v);
    else
        me->verts= (mvert)malloc(me->size_v * sizeof(struct M_Vert));
    assert(me->verts);
}


static void me_expand_edges(mesh me, uint count)
{
    assert(me);
    assert(count);
    me->size_e+= count;
    if (me->edges)
        me->edges= (medge)realloc(me->edges, me->size_e);
    else
        me->edges= (medge)malloc(me->size_e * sizeof(struct M_Edge));
    assert(me->edges);
}

static void me_expand_faces(mesh me, uint count)
{
    assert(me);
    assert(count);
    me->size_f+= count;
    if (me->faces)
        me->faces= (mface)realloc(me->faces, me->size_f);
    else
        me->faces= (mface)malloc(me->size_f * sizeof(struct M_Face));
    assert(me->faces);
}

mface me_add_face(mesh me, uint vx0, uint vx1, uint vx2)
{
    assert(me);
    if (vx0 > me->last_v || vx1 > me->last_v || vx2 > me->last_v ||
        vx0 == vx1 || vx1 == vx2 || vx2 == vx1 || !me->verts)
        return NULL;
    if (!me->faces)
        me->faces= (mface)malloc((me->size_f= ME_DEFAULT_SIZE_F) * sizeof(struct M_Face));
    else if (me->last_f == me->size_f)
        me_expand_faces(me, me->size_f * 2);
    
    mface result= me->faces + me->last_f++;
    
    result->verts[0]= me->verts + vx0;
    result->verts[1]= me->verts + vx0;
    result->verts[2]= me->verts + vx0;
    
    medge tmp;
    tmp= result->edges[0]= me_add_edge(me, vx0, vx1);
    fl_append(&tmp->users, result);
    tmp= result->edges[1]= me_add_edge(me, vx1, vx2);
    fl_append(&tmp->users, result);
    tmp= result->edges[2]= me_add_edge(me, vx2, vx1);
    fl_append(&tmp->users, result);
    
    result->flags= 0;
    return result;
}

int me_del_face(mesh me, uint idx)
{
    assert(me);
    if (idx < me->last_f){
        assert(me->faces);
        mface f= me->faces + idx++;
        for (;idx < me->last_f; idx++)
            *f++= me->faces[idx];
        me->last_f--;
        return 1;
    }
    return 0;
}

int me_kill_face(mesh me, uint idx)
{
    assert(me);
    if (idx < me->last_f){
        assert(me->faces);
        mface f= me->faces + idx;
        //me_del_vert will kill the face
        //need to store verts elsewere
        mvert v0= f->verts[0];
        mvert v1= f->verts[1];
        mvert v2= f->verts[2];
        assert(me->verts);
        me_del_vert(me, v0 - me->verts);
        me_del_vert(me, v1 - me->verts);
        me_del_vert(me, v2 - me->verts);
        me_del_face(me, idx);
        return 1;
    }
    return 0;
}

mvert me_add_vert(mesh me, vec * co)
{
    assert(co);
    assert(me);
    if (!me->verts){
        me->size_v= ME_DEFAULT_SIZE_V;
        me->verts= (mvert)malloc(me->size_v * sizeof(struct M_Vert));
        v_copy(&me->bb.min, co);
        v_copy(&me->bb.max, co);
    }
    else if(me->last_v == me->size_v){
        me_expand_verts(me, me->size_v * 2);
    }
    
    mvert result= me->verts + me->last_v++;
    v_copy(&result->co, co);
    check_bounds(&me->bb, co);
    result->flags= 0;
    return result;
}

int me_del_vert(mesh me, uint idx)
{
    assert(me);
    if (idx < me->last_v){
        assert(me->verts);
        mvert vx= me->verts + idx;
        if(me->edges && me->last_e){
            uint users[me->last_e];
            uint count= me_vertex_edges(me, vx, users);
            for(;count; me_del_edge(me, users[--count]))
                ;
        }
        vx= me->verts + idx++;
        for(;idx < me->last_v; idx++)
            *vx++=me->verts[idx];
        me->last_v--;
        me_recalc_bounds(me);
        return 1;
    }
    return 0;   
}

medge me_add_edge(mesh me, uint vx0, uint vx1)
{
    assert(me);
    assert(me->verts);
    if (vx0 > me->last_e || vx1 > me->last_e || vx0 == vx1)
        return NULL;
    if (!me->edges)
        me->edges= (medge)malloc((me->size_e= ME_DEFAULT_SIZE_E) * sizeof(struct M_Edge));
    else if(me->last_e == me->size_e)
        me_expand_edges(me, me->size_e * 2);

    medge result= me_find_edge(me, vx0, vx1);
    if (!result){
        result= me->edges + me->last_e++;
        result->v0= me->verts + vx0; 
        result->v1= me->verts + vx1;
        result->users= NULL;
    }
    return result; 
}

medge me_find_edge(mesh me, uint vx0, uint vx1)
{
    assert(me);
    assert(me->verts);
    assert(me->edges);
    if (!(vx0 != vx1 && vx0 < me->last_v && vx1 < me->last_v ))
        return NULL;
        
    if (vx0 > vx1){
        uint tmp= vx1;
        vx1= vx0;
        vx0= tmp;
    }
    unsigned int i;
    medge result;
   
    mvert test0= me->verts + vx0;
    mvert test1= me->verts + vx1;
    
    for (i=0; i < me->last_e; i++){
        result= me->edges + i;
        if (result->v0 == test0 && result->v1 == test1)
            return result;
    }
    return NULL;
}

int me_del_edge(mesh me, uint idx)
{
    assert(me);
    assert(me->edges);
    if (idx < me->last_e){
        medge ed= me->edges + idx;
        if (ed->users){
            uint i;
            for (i=0; i < ed->users->last; i++)
                me_del_face(me, ed->users->faces[i] - me->faces);
            fl_kill(&ed->users);
        }
        ed= me->edges + idx++;
        for(;idx < me->last_e; idx++)
            *ed++= me->edges [idx];
        me->last_e--;
        return 1;
    }
    else
        return 0;
}

int me_kill_edge(mesh me, uint idx)
{
    assert(me);
    if (idx < me->last_e){
        medge e= me->edges + idx;
        uint v0= e->v0 - me->verts;
        uint v1= e->v1 - me->verts;
        me_del_vert(me, v0);
        me_del_vert(me, v1);
        return 1;
    }
    return 0;
}

uint me_edge_faces(mesh me, medge ed, uint *users)
{
    assert(me);
    assert(me->faces);
    assert(users);
    uint count=0;
    uint i, j;
    mface f;
    for (i=0; i < me->last_f; i++){
        f= me->faces + i;
        for(j=0; j < 3; j++){
            if (ed==f->edges[j])
                users[count++]= i;
        }
    }
    return count;
}

uint me_vertex_edges(mesh me, mvert vx, uint *users)
{
    assert(me);
    assert(vx);
    assert(users);
    uint count= 0;
    uint i;
    medge e;
    for (i=0; i < me->last_e; i++){
        e= me->edges + i;
        if (e->v0 == vx || e->v1 == vx)
            users[count++]= i;
    }
    return count;
}

void mf_normal(mface f, vec *no)
{
    assert(f);
    assert(no);
    vec tmp;
    v_zero(no);
    v_cross(&tmp, &f->verts[0]->co, &f->verts[1]->co);
    v_add(no, no, &tmp);
    v_cross(&tmp, &f->verts[1]->co, &f->verts[2]->co);
    v_add(no, no, &tmp);
    v_cross(&tmp, &f->verts[2]->co, &f->verts[0]->co);
    v_add(no, no, &tmp);
    
    /*
     * uint i, j;
    for (i=2, j=0; j < 3; i=j++){
        v_cross(&tmp, &f->verts[i->co, &f->verts[j]->co);
        v_add(no, no, &tmp);
    }
    */
}

facelist mf_adjascent_faces(mface f)
{
    assert(f);
    facelist result=NULL, tmp;
    uint i, j;
    for(i=0; i < 3; i++){
        tmp= f->edges[i]->users;
        for (j=0; j < tmp->last; j++){
            if (tmp->faces[i] != f){
                fl_append(&result, tmp->faces[i]);
            }
        }
    }
    return result;
}

polylist me_to_polys(mesh me, double thresh, uint *coplanar_id)
{
    island islands= NULL;
    uint inum= me_find_islands(me, &islands, thresh);
    uint i;
    polylist result= pll_init(inum);
    polygon tmp;
    island isl;
    for (i=0; i < inum; i++){
        isl= islands + i;
        tmp= island_to_poly(isl, coplanar_id[0]++);
        pll_append(&result, tmp);
        island_kill(&isl);
    }
    return result;
}

//////////// FACELIST UTILS

static facelist fl_check_pointer(facelist *fl_pt)
{
        assert(fl_pt);
        facelist fl= *fl_pt;
        assert(fl);
        return fl; 
}

static void fl_expand(facelist fl, unsigned int new_size)
{
    assert(fl);
    fl->faces= (mface *)realloc(fl->faces, new_size * sizeof(mface));
    assert(fl->faces);
    fl->size= new_size;
}

facelist fl_init(unsigned int count)
{
    facelist result= (facelist)malloc(sizeof(struct FaceList));
    result->size= count ? count : 4;
    result->faces= (mface*)malloc(result->size * sizeof(mface));
    result->last= 0;
    return result; 
}

void fl_append(facelist *fl_pt, mface face)
{
        facelist fl= fl_check_pointer(fl_pt);
        assert(fl);
        if (fl->last == fl->size)
                fl_expand(fl, 2 * fl->size);
        fl->faces[fl->last++]= face;
}

void fl_extend(facelist *fl_pt, mface *faces, unsigned int count)
{
        facelist fl= fl_check_pointer(fl_pt);
        if (fl->last + count >= fl->size)
                fl_expand(fl, (2 * fl->size) + count);
        unsigned int i;
        for (i=0; i < count; i++)
                fl->faces[fl->last++]= faces[i];
}

mface fl_pop(facelist fl)
{
        assert(fl);
        if (fl->last)
                return fl->faces[--fl->last];
        else
                return NULL;
}

mface fl_remove(facelist fl, unsigned int idx)
{
    assert(fl);
    if (idx < --fl->last){
        mface result= fl->faces[idx];
        mface *prv;
        for (;idx < fl->last;){
            prv= fl->faces + idx++;
            *prv= *(fl->faces + idx);
        }       
        return result;
    }
    return NULL;
}
        
int fl_remove_f(facelist fl, mface face)
{
    assert(fl);
    uint i;
    for (i=0; i < fl->last; i++){
        if (fl->faces[i] == face){
            fl_remove(fl, i);
            return 1;
            break;
        }
    }
    return 0;
    
}

void fl_kill(facelist *fl_pt)
{
    assert(fl_pt);
    facelist fl= *fl_pt;
    if(fl){
        if (fl->faces)
            free(fl->faces);
        free(fl);
        *fl_pt=NULL;
    }
}

void fl_killall(facelist *fl_pt)
{
    assert(fl_pt);
    facelist fl= *fl_pt;
    if (fl){
        if (fl->faces){
            for(; fl->last; fl->last--)
                free(fl->faces[fl->last]);
            free(fl->faces);
        }
        free(fl);
        *fl_pt= NULL;
    }
}

void me_mark_creases(mesh me, float angle){
	uint i;
	medge e;
	vec no_a, no_b;
	while(angle > 180.0)
		angle-= 180.0;
	angle*= DEG2RAD;
	for (i=0; i < me->last_e; i++){
		e= me->edges + i;
		if (e->users->last > 2)
			continue;
		mf_normal(e->users->faces[0], &no_a);
		mf_normal(e->users->faces[1], &no_b);
		if (v_angle_r(&no_a, &no_b) < angle)
			e->flags|= CREASE;
	}
}

